<?php

declare(strict_types=1);

namespace N98\Magento\Command\Developer\Module;

use InvalidArgumentException;
use N98\Magento\Command\AbstractMagentoCommand;
use N98\Util\Console\Helper\TwigHelper;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Create module skeleton command
 *
 * @package N98\Magento\Command\Developer\Module
 */
class CreateCommand extends AbstractMagentoCommand
{
    protected array $twigVars = [];

    protected string $baseFolder;

    protected string $moduleDirectory;

    protected string $vendorNamespace;

    protected string $moduleName;

    protected string $codePool;

    protected bool $modmanMode = false;

    private OutputInterface $output;

    protected function configure(): void
    {
        $this
            ->setName('dev:module:create')
            ->addArgument('vendorNamespace', InputArgument::REQUIRED, 'Namespace (your company prefix)')
            ->addArgument('moduleName', InputArgument::REQUIRED, 'Name of your module.')
            ->addArgument('codePool', InputArgument::OPTIONAL, 'Codepool (local, community)', 'local')
            ->addOption('add-controllers', null, InputOption::VALUE_NONE, 'Adds controllers')
            ->addOption('add-blocks', null, InputOption::VALUE_NONE, 'Adds blocks')
            ->addOption('add-helpers', null, InputOption::VALUE_NONE, 'Adds helpers')
            ->addOption('add-models', null, InputOption::VALUE_NONE, 'Adds models')
            ->addOption('add-setup', null, InputOption::VALUE_NONE, 'Adds SQL setup')
            ->addOption('add-all', null, InputOption::VALUE_NONE, 'Adds blocks, helpers and models')
            ->addOption('modman', null, InputOption::VALUE_NONE, 'Create all files in folder with a modman file.')
            ->addOption('add-readme', null, InputOption::VALUE_NONE, 'Adds a readme.md file to generated module')
            ->addOption('add-composer', null, InputOption::VALUE_NONE, 'Adds a composer.json file to generated module')
            ->addOption('author-name', null, InputOption::VALUE_OPTIONAL, 'Author for readme.md or composer.json')
            ->addOption('author-email', null, InputOption::VALUE_OPTIONAL, 'Author for readme.md or composer.json')
            ->addOption('description', null, InputOption::VALUE_OPTIONAL, 'Description for readme.md or composer.json')
            ->setDescription('Create and register a new magento module.');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->output = $output;

        $this->modmanMode = $input->getOption('modman');
        if ($input->getOption('add-all')) {
            $input->setOption('add-controllers', true);
            $input->setOption('add-blocks', true);
            $input->setOption('add-helpers', true);
            $input->setOption('add-models', true);
            $input->setOption('add-setup', true);
            $input->setOption('add-readme', true);
            $input->setOption('add-composer', true);
        }

        if (!$this->modmanMode) {
            $this->detectMagento($output);
        }

        $this->baseFolder = __DIR__ . '/../../../../../../res/module/create';

        $this->vendorNamespace = ucfirst($input->getArgument('vendorNamespace'));
        $this->moduleName = ucfirst($input->getArgument('moduleName'));
        $this->codePool = $input->getArgument('codePool');
        if (!in_array($this->codePool, ['local', 'community'])) {
            throw new InvalidArgumentException('Code pool must "community" or "local"');
        }

        $this->initView($input);
        $this->createModuleDirectories($input, $output);
        $this->writeEtcModules($output);
        $this->writeModuleConfig($output);
        $this->writeReadme($input, $output);
        if ($this->modmanMode) {
            $this->writeModmanFile($output);
        }

        $this->writeComposerConfig($input, $output);
        $this->addAdditionalFiles($output);

        return Command::SUCCESS;
    }

    protected function initView(InputInterface $input): void
    {
        $this->twigVars = [
            'vendorNamespace'   => $this->vendorNamespace,
            'moduleName'        => $this->moduleName,
            'codePool'          => $this->codePool,
            'createControllers' => $input->getOption('add-controllers'),
            'createBlocks'      => $input->getOption('add-blocks'),
            'createModels'      => $input->getOption('add-models'),
            'createHelpers'     => $input->getOption('add-helpers'),
            'createSetup'       => $input->getOption('add-setup'),
            'authorName'        => $input->getOption('author-name'),
            'authorEmail'       => $input->getOption('author-email'),
            'description'       => $input->getOption('description'),
        ];
    }

    protected function createModuleDirectories(InputInterface $input, OutputInterface $output): void
    {
        if ($this->modmanMode) {
            $modManDir = $this->vendorNamespace . '_' . $this->moduleName . '/src';
            if (file_exists($modManDir)) {
                throw new RuntimeException('Module already exists. Stop.');
            }

            mkdir($modManDir, 0777, true);
            $this->_magentoRootFolder = './' . $modManDir;
            mkdir($this->_magentoRootFolder . '/app/etc/modules', 0777, true);
        }

        $moduleDir = sprintf(
            '%s/app/code/%s/%s/%s',
            $this->_magentoRootFolder,
            $this->codePool,
            $this->vendorNamespace,
            $this->moduleName,
        );

        if (file_exists($moduleDir)) {
            throw new RuntimeException('Module already exists. Stop.');
        }

        $this->moduleDirectory = $moduleDir;
        mkdir($this->moduleDirectory, 0777, true);
        $output->writeln('<info>Created directory: <comment>' . $this->moduleDirectory . '<comment></info>');

        // Add etc folder
        $this->createModuleDirectory('etc');

        // Add controllers folder
        if ($input->getOption('add-controllers')) {
            $this->createModuleDirectory('controllers');
        }

        // Add blocks folder
        if ($input->getOption('add-blocks')) {
            $this->createModuleDirectory('Block');
        }

        // Add helpers folder
        if ($input->getOption('add-helpers')) {
            $this->createModuleDirectory('Helper');
        }

        // Add models folder
        if ($input->getOption('add-models')) {
            $this->createModuleDirectory('Model');
        }

        // Create SQL and Data folder
        if ($input->getOption('add-setup')) {
            $sqlSetupFolder = $this->moduleDirectory . '/sql/' . strtolower($this->vendorNamespace) . '_' .
                strtolower($this->moduleName) . '_setup';
            mkdir($sqlSetupFolder, 0777, true);
            $output->writeln('<info>Created directory: <comment>' . $sqlSetupFolder . '<comment></info>');

            $dataSetupFolder = $this->moduleDirectory . '/data/' . strtolower($this->vendorNamespace) . '_' .
                strtolower($this->moduleName) . '_setup';
            mkdir($dataSetupFolder, 0777, true);
            $output->writeln('<info>Created directory: <comment>' . $dataSetupFolder . '<comment></info>');
        }
    }

    private function createModuleDirectory(string $directory): void
    {
        $path = $this->moduleDirectory . '/' . $directory;
        mkdir($path);
        $this->output->writeln('<info>Created directory: <comment>' . $path . '<comment></info>');
    }

    protected function writeEtcModules(OutputInterface $output): void
    {
        $outFile = sprintf(
            '%s/app/etc/modules/%s_%s.xml',
            $this->_magentoRootFolder,
            $this->vendorNamespace,
            $this->moduleName,
        );

        /** @var TwigHelper $helper */
        $helper = $this->getHelper('twig');
        $buffer = $helper->render('dev/module/create/app/etc/modules/definition.twig', $this->twigVars);
        $size = file_put_contents($outFile, $buffer);

        $output->writeln('<info>Created file: <comment>' . $outFile . '<comment> (' . $size . ' bytes)</info>');
    }

    protected function writeModuleConfig(OutputInterface $output): void
    {
        $outFile    = $this->moduleDirectory . '/etc/config.xml';
        /** @var TwigHelper $twigHelper */
        $twigHelper = $this->getHelper('twig');
        file_put_contents(
            $outFile,
            $twigHelper->render('dev/module/create/app/etc/modules/config.twig', $this->twigVars),
        );

        $output->writeln('<info>Created file: <comment>' . $outFile . '<comment></info>');
    }

    protected function writeModmanFile(OutputInterface $output): void
    {
        $outFile    = $this->_magentoRootFolder . '/../modman';
        /** @var TwigHelper $twigHelper */
        $twigHelper = $this->getHelper('twig');
        file_put_contents(
            $outFile,
            $twigHelper->render('dev/module/create/modman.twig', $this->twigVars),
        );
        $output->writeln('<info>Created file: <comment>' . $outFile . '<comment></info>');
    }

    /**
     * Write standard readme
     *
     * TODO: Make author name / company URL and more configurable
     *
     * @see https://raw.github.com/sprankhub/Magento-Extension-Sample-Readme/master/readme.markdown
     */
    protected function writeReadme(InputInterface $input, OutputInterface $output): void
    {
        if (!$input->getOption('add-readme')) {
            return;
        }

        $outFile    = $this->modmanMode ? $this->_magentoRootFolder . '/../readme.md' : $this->moduleDirectory . '/etc/readme.md';
        /** @var TwigHelper $twigHelper */
        $twigHelper = $this->getHelper('twig');
        file_put_contents(
            $outFile,
            $twigHelper->render('dev/module/create/app/etc/modules/readme.twig', $this->twigVars),
        );
        $output->writeln('<info>Created file: <comment>' . $outFile . '<comment></info>');
    }

    /**
     * Write composer.json
     */
    protected function writeComposerConfig(InputInterface $input, OutputInterface $output): void
    {
        if (!$input->getOption('add-composer')) {
            return;
        }

        if ($this->modmanMode) {
            $outFile = $this->_magentoRootFolder . '/../composer.json';
        } else {
            $outFile = $this->moduleDirectory . '/etc/composer.json';
        }

        /** @var TwigHelper $twigHelper */
        $twigHelper = $this->getHelper('twig');

        file_put_contents(
            $outFile,
            $twigHelper->render('dev/module/create/composer.twig', $this->twigVars),
        );
        $output->writeln('<info>Created file: <comment>' . $outFile . '<comment></info>');
    }

    protected function addAdditionalFiles(OutputInterface $output): void
    {
        $config = $this->getCommandConfig();
        if (isset($config['additionalFiles']) && is_array($config['additionalFiles'])) {
            foreach ($config['additionalFiles'] as $template => $outFileTemplate) {
                $outFile = $this->getOutfile($outFileTemplate);
                $outFileDir = dirname($outFile);
                if (!is_dir($outFileDir)) {
                    mkdir($outFileDir, 0777, true);
                }

                /** @var TwigHelper $twigHelper */
                $twigHelper = $this->getHelper('twig');
                file_put_contents($outFile, $twigHelper->render($template, $this->twigVars));

                $output->writeln('<info>Created file: <comment>' . $outFile . '<comment></info>');
            }
        }
    }

    private function getOutfile(string $filename): string
    {
        $paths = ['rootDir'   => $this->_magentoRootFolder, 'moduleDir' => $this->moduleDirectory];

        /** @var TwigHelper $twig */
        $twig = $this->getHelper('twig');
        return $twig->renderString($filename, array_merge($this->twigVars, $paths));
    }
}
